package challenges;

import javax.microedition.khronos.opengles.GL10;

import util.GameClock;
import util.GameVars;
import util.Logger;
import util.VertexBuffer;
import android.graphics.PointF;
import game.Challenge;
import gfx.Glyphs;
import gfx.Shapes;

public class DontLetUp extends Challenge{
	private PointF messageLoc = new PointF(5, 1.5f), scoreLoc = new PointF(5, 2.5f), playLoc = new PointF(5, 2.5f);
	private Player player;
	private long timer;
	
	private long startTimer;
	private long startDelay = 500;
	
	private boolean roundReady;
	private boolean starting;
	private boolean letUp;
	
	private short borderIterator;
	
	private EnemyManager em;
	
	public DontLetUp(){
		roundReady = false;
		starting = false;
		
		player = new Player();
		
		em = new EnemyManager();
	}
	
	public void draw(GL10 gl){
		gl.glClearColor(0, 0, 0, 1);
		
		drawBorder(gl);
			
		if(!roundReady && !starting){
			gl.glColor4f(1, 0, 0, 1);
			Glyphs.drawString(gl, "Hold to play", playLoc, GameVars.TEXT_ALIGNMENT_CENTER);
		}
		
		drawTimer(gl);
		
		player.draw(gl);
		
		em.draw(gl);

		super.draw(gl);
		
		if(isGameOver){
			Glyphs.drawString(gl, "Final Time: " + (timer / 60000) + " : " + ((timer /1000) % 60 < 10 ? "0" : "") + ((timer / 1000) % 60) + "." + (timer % 1000 < 10 ? "00" : timer % 1000 < 100 ? "0" : "") + (timer % 1000), scoreLoc, GameVars.TEXT_ALIGNMENT_CENTER);
			if(letUp)
				Glyphs.drawString(gl, "Next time, don't let up!", messageLoc, GameVars.TEXT_ALIGNMENT_CENTER);
		}
	}
	
	public void drawBorder(GL10 gl){
		for(borderIterator = 0; borderIterator <= 18; ++borderIterator)
			Shapes.drawSquare(gl, 5, borderIterator * .333f, 5, .01f, .6f, 0, .6f, 1);
		for(borderIterator = 0; borderIterator <= 30; ++borderIterator)
			Shapes.drawSquare(gl, borderIterator * .333f, 3, .01f, 4, .6f, 0, .6f, 1);
	}
	
	public void update(long time){
		if(reloading)
			reset();
		super.update(time);
		player.update(time);
		if(roundReady)
			timer += GameClock.getInstance().getTick();
		
		em.addStuff(timer);

		if(em.collisionCheck(player))
			isGameOver = true;
		em.update(time);
	}
	
	public boolean handleUp(PointF location) {
		if(super.handleUp(location))
			return false;
		if(!roundReady)
			starting = false;
		else{
			if(isGameOver != true){
				isGameOver = true;
				letUp = true;
			}
		}
		return true;
	}

	public boolean handleDrag(PointF location) {
		if(super.handleDrag(location))
			return false;
		if(roundReady && !isPaused())
			player.setLoc(location);
		return true;
	}

	public boolean handleTouch(PointF location) {
		if(super.handleTouch(location))
			return false;
		if(starting){
			if(GameClock.getInstance().getCurrentTime() - startTimer > startDelay)
				roundReady = true;
		}
		else if(Math.sqrt((location.x - 5) * (location.x - 5) + (location.y - 3) * (location.y - 3)) < player.getRadius()){
			startTimer = GameClock.getInstance().getCurrentTime();
			starting = true;
		}
		return true;
	}
	
	private void reset(){
		roundReady = false;
		starting = false;
		roundReady = false;
		timer = 0;
		player.reset();
		em.reset();
	}
	
	private void drawTimer(GL10 gl){
		gl.glPushMatrix();
		gl.glTranslatef(5, 5.5f, 0);
		Glyphs.drawString(gl, "" + (timer / 60000) + " : " + ((timer /1000) % 60 < 10 ? "0" : "") + ((timer / 1000) % 60) + "." + (timer % 1000 < 10 ? "00" : timer % 1000 < 100 ? "0" : "") + (timer % 1000), null, GameVars.TEXT_ALIGNMENT_CENTER);
		gl.glPopMatrix();
	}
	
	class Player{
		private PointF location;
		private float radius;
		private float rotation;
		float[] squareone = {.15f, .1f, 0, .2f, .1f, 0, .15f, .2f, 0, .2f, .2f, 0};
		float[] squaretwo = {-.2f, .1f, 0, -.15f, .1f, 0, -.2f, .2f, 0, -.15f, .2f, 0};
		float[] squarethree = {.15f, -.2f, 0, .2f, -.2f, 0, .15f, -.1f, 0, .2f, -.1f, 0};
		float[] squarefour = {-.2f, -.2f, 0, -.15f, -.2f, 0, -.2f, -.1f, 0, -.15f, -.1f, 0};
		
		float[] squarefive = {.1f, .15f, 0, .2f, .15f, 0, .1f, .2f, 0, .2f, .2f, 0};
		float[] squaresix = {.1f, -.2f, 0, .2f, -.2f, 0, .1f, -.15f, 0, .2f, -.15f, 0};
		float[] squareseven = {-.2f, .15f, 0, -.1f, .15f, 0, -.2f, .2f, 0, -.1f, .2f, 0};
		float[] squareeight = {-.2f, -.2f, 0, -.1f, -.2f, 0, -.2f, -.15f, 0, -.1f, -.15f, 0};

		public Player(){
			location = new PointF(5, 3);
			radius = (float) Math.sqrt(2 * (.2 * .2));
		}
		
		public void draw(GL10 gl){
			gl.glPushMatrix();
			
			gl.glTranslatef(location.x, location.y, 0);	
			gl.glRotatef(rotation, 0, 0, 1);
			
			gl.glDisable(GL10.GL_TEXTURE_2D);
			gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
			gl.glFrontFace(GL10.GL_CCW);
			gl.glColor4f(1, 0, 0, 1);

			drawBits(gl);
			
			gl.glRotatef(45, 0, 0, 1);

			drawBits(gl);
			
			if(starting && !roundReady){
				gl.glScalef(5 - (GameClock.getInstance().getCurrentTime() - startTimer) / 125f, 5 - (GameClock.getInstance().getCurrentTime() - startTimer) / 125f, 1);

				gl.glColor4f(.95f, .33f, .33f, 1);

				drawBits(gl);
				
				gl.glRotatef(45, 0, 0, 1);

				drawBits(gl);
			}
			
			gl.glColor4f(1, 1, 1, 1);
			gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
			gl.glEnable(GL10.GL_TEXTURE_2D);
			gl.glPopMatrix();
		}
		
		private void drawBits(GL10 gl){
			drawSquare(gl, squareone);
			drawSquare(gl, squaretwo);
			drawSquare(gl, squarethree);
			drawSquare(gl, squarefour);
			drawSquare(gl, squarefive);
			drawSquare(gl, squaresix);
			drawSquare(gl, squareseven);
			drawSquare(gl, squareeight);
		}
		
		private void drawSquare(GL10 gl, float[] square){
			gl.glVertexPointer(3, GL10.GL_FLOAT, 0, VertexBuffer.getVertexBuffer(square));
			gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, square.length / 3);
			
		}
		
		public void update(long time){
			rotation += (180 * time) / 1000f;
		}
		
		public void setLoc(PointF loc){
			location.set(loc);
		}
		
		public float getRadius(){ return radius; }
		public PointF getLoc(){ return location; }
		public void reset(){ location.set(5, 3); letUp = false; };
	}
	
	class Arrow extends Enemy{
		private float fadeIn = 750;
		private short collisionIterator;
		
		float[] archive;
		
		private float s;
		private float c;
		
		public Arrow(float x, float y, float rotation){
			speed = 0.004f;
			location = new PointF(x, y);
			
			vertices = new float[18];
			vertices[2] = vertices[5] = vertices[8] = vertices[11] = vertices[14] = vertices[17]= 0;
			vertices[0] = -.1f;
			vertices[1] = -.2f;
			vertices[3] = vertices[12] = 0f;
			vertices[4] = vertices[13] = -.1f;
			
			vertices[6] = vertices[9] = 0f;
			vertices[7] = vertices[10] = .2f;
			
			vertices[15] = .1f;
			vertices[16] = -.2f;		
			
			archive = vertices.clone();
		}
		
		public void draw(GL10 gl){
			gl.glColor4f(1, .6f, 0, 1);
			super.draw(gl);
		}
		
		public void update(long time){
			if(isActive){
				if(!inBounds()){
//					Logger.log("Removed Arrow");
					isActive = false;
					isDead = true;
				}
				else location.set((float) (location.x + (time * -s * speed)), (float) (location.y + time * c * speed));
			}
			else{ 
				scale = (3 - (GameClock.getInstance().getCurrentTime() - addedTime) / fadeIn * 2);
				alpha = ((GameClock.getInstance().getCurrentTime() - addedTime) / fadeIn);
				if(GameClock.getInstance().getCurrentTime() - addedTime > fadeIn){
					alpha = 1;
					scale = 1; 
					isActive = true; 
				} 
			}
		}
		
		public boolean inBounds(){
			if(location.x < -.5 || location.x > 10.5 || location.y < -.5 || location.y > 6.5)
				return false;
			return true;
		}
		
		public void setRotation(float rot){
			c = (float) Math.cos(Math.toRadians(rot));
			s = (float) Math.sin(Math.toRadians(rot));
			float oldX = 0;
			
			for(int r = 0; r < vertices.length; ++r)
				if(r % 3 == 0){ // x
					oldX = vertices[r];
					vertices[r] = (float) (vertices[r] * Math.cos(Math.toRadians(rot)) - vertices[r + 1] * Math.sin(Math.toRadians(rot)));
				}
				else if(r % 3 == 1) // y
					vertices[r] = (float) (oldX * Math.sin(Math.toRadians(rot)) + vertices[r] * Math.cos(Math.toRadians(rot)));
		}
		
		public boolean isDead(){ return isDead; }
		public boolean collisionCheck(float x, float y, float r){
			if(!isActive)
				return false;
			for(collisionIterator = 0; collisionIterator < vertices.length / 3; ++collisionIterator){
				if(Math.sqrt(((location.x + vertices[collisionIterator * 3]) - (x)) * ((location.x + vertices[collisionIterator * 3]) - (x)) + (location.y + vertices[collisionIterator * 3 + 1] - y) * (location.y + vertices[collisionIterator * 3 + 1] - y)) < r)
					return true;
			}
			return false;
		}
		public void add(){
			super.add();
			isActive = false;
			vertices = archive.clone();
		}
	}
	
	// REMOVED.
	class Cluster extends Enemy{
		private short detail = 16;
		private float radius = .25f;
		@SuppressWarnings("unused")
		private float fuse;
		
		public Cluster(float x, float y, float dx, float dy, float fuse){
			location = new PointF(x, y);
			this.fuse = fuse;

			vertices = new float[detail * 18];
			float theta = (float)((float) 3.1415926) / ((float)detail); 
			float c = (float) Math.cos(theta);
			float s = (float) Math.sin(theta);
			float rx, ry, rxtwo, rytwo;

			rx = radius * c;
			ry = radius * s; 

			rxtwo = (radius - .05f) * c;
			rytwo = (radius - .05f) * s; 
			
			for(int i = 0; i < detail; ++i){
				vertices[i * 18] = rx;
				vertices[i * 18 + 1] = ry;

				vertices[i * 18 + 3] = vertices[i * 18 + 9] = rxtwo;
				vertices[i * 18 + 4] = vertices[i * 18 + 10] = rytwo;

				vertices[i * 18 + 6] = vertices[i * 18 + 15] = rx;
				vertices[i * 18 + 7] = vertices[i * 18 + 16] = -ry;

				vertices[i * 18 + 12] = rxtwo;
				vertices[i * 18 + 13] = -rytwo;
				
				vertices[i * 18 + 2] = vertices[i * 18 + 5] = vertices[i * 18 + 8] = vertices[i * 18 + 11] = vertices[i * 18 + 14] = vertices[i * 18 + 17] = 0;
			} 
			
			float oldX;
			for(int i = 0; i < vertices.length / 3; ++i){
				Logger.log("" + theta * (i / 6) * 2);
				oldX = vertices[i * 3];
				vertices[i * 3] = (float) (vertices[i * 3] * Math.cos(theta * (i / 6) * 2) - vertices[i * 3 + 1] * Math.sin(theta * (i / 6) * 2));
				vertices[i * 3 + 1] = (float) (oldX * Math.sin(theta * (i / 6) * 2) + vertices[i * 3 + 1] * Math.cos(theta * (i / 6) * 2));
			}
		}
		
		public void draw(GL10 gl){
			gl.glColor4f(1, 1, 0, 1);
			super.draw(gl);
		}
		
		public void update(long time){
		}
		
		public void setFuse(float f){ fuse = f; }
	}
	
	class ArcSquare extends Enemy{
		private float fadeIn = 1000;
		private float radius;
		private float width = .2f;
		private float cCheck = (float) Math.sqrt((.2 * .2) * 2);
		private byte start; // 0 = left, 1 = top, 2 = right, 3 = bottom
		private boolean clockwise;
		
		public ArcSquare(float x, float y, float radius, boolean cw){
			clockwise = cw;
			location = new PointF(x, y);
			speed = .001f;
			
			vertices = new float[18];

			for(int z = 2; z < vertices.length; z+=3)
				vertices[z] = 0;
			
			vertices[0] = -width * .5f;
			vertices[1] = -width * .5f;
			
			vertices[3] = vertices[9] = width * .5f;
			vertices[4] = vertices[10] = -width * .5f;
			
			vertices[6] = vertices[15] = -width * .5f;
			vertices[7] = vertices[16] = width * .5f;
			
			vertices[12] = width * .5f;
			vertices[13] = width * .5f;
		}
		
		public void draw(GL10 gl){
			gl.glColor4f(0, 1, 1, alpha);
			super.draw(gl);
		}
		
		public void update(long time){
			rotation += (clockwise ? -1 : 1) * (360 * time) / 1000f;
			
			if(isActive){
				if(!inBounds()){
//					Logger.log("Removed Arc");
					isActive = false; 
					isDead = true;
				}
				switch(start){
				case 0: // Left
					if(clockwise)
						location.set(radius * (float) Math.sin(speed * (GameClock.getInstance().getCurrentTime() - (addedTime + (long) fadeIn))), radius * (float) Math.cos(speed * (GameClock.getInstance().getCurrentTime() - (addedTime + (long) fadeIn))));
					else
						location.set(radius * (float) Math.sin(speed * (GameClock.getInstance().getCurrentTime() - (addedTime + (long) fadeIn))), 6 - radius * (float) Math.cos(speed * (GameClock.getInstance().getCurrentTime() - (addedTime + (long) fadeIn))));
					break;
				case 1: // Top
					if(clockwise)
						location.set(radius * (float) Math.cos(speed * (GameClock.getInstance().getCurrentTime() - (addedTime + (long) fadeIn))), 6 - radius * (float) Math.sin(speed * (GameClock.getInstance().getCurrentTime() - (addedTime + (long) fadeIn))));
					else
						location.set(10 - radius * (float) Math.cos(speed * (GameClock.getInstance().getCurrentTime() - (addedTime + (long) fadeIn))), 6 - radius * (float) Math.sin(speed * (GameClock.getInstance().getCurrentTime() - (addedTime + (long) fadeIn))));
					break;
				case 2: // Right
					if(clockwise)
						location.set(10 - radius * (float) Math.sin(speed * (GameClock.getInstance().getCurrentTime() - (addedTime + (long) fadeIn))), 6 - radius * (float) Math.cos(speed * (GameClock.getInstance().getCurrentTime() - (addedTime + (long) fadeIn))));
					else
						location.set(10 - radius * (float) Math.sin(speed * (GameClock.getInstance().getCurrentTime() - (addedTime + (long) fadeIn))), radius * (float) Math.cos(speed * (GameClock.getInstance().getCurrentTime() - (addedTime + (long) fadeIn))));
					break;
				default: // Bottom
					if(clockwise)
						location.set(10 - radius * (float) Math.cos(speed * (GameClock.getInstance().getCurrentTime() - (addedTime + (long) fadeIn))), radius * (float) Math.sin(speed * (GameClock.getInstance().getCurrentTime() - (addedTime + (long) fadeIn))));
					else
						location.set(radius * (float) Math.cos(speed * (GameClock.getInstance().getCurrentTime() - (addedTime + (long) fadeIn))), radius * (float) Math.sin(speed * (GameClock.getInstance().getCurrentTime() - (addedTime + (long) fadeIn))));
					break;
				}
			}
			else{ 
				scale = (3 - (GameClock.getInstance().getCurrentTime() - addedTime) / fadeIn * 2);
				alpha = ((GameClock.getInstance().getCurrentTime() - addedTime) / fadeIn);
				if(GameClock.getInstance().getCurrentTime() - addedTime > fadeIn){
					alpha = 1;
					scale = 1; 
					isActive = true; 
				} 
			}
		}
		
		private boolean inBounds(){
			if(location.x + width < 0 || location.x - width > 10 || location.y + width < 0 || location.y - width > 6)
				return false;
			return true;
		}
		
		public void setRadius(float r, boolean cw, byte s){ radius = r; clockwise = cw; start = s; }
		
		public boolean collisionCheck(float x, float y, float r){
			if(!isActive)
				return false;
			if(Math.abs(location.x - x) > .5f || Math.abs(location.y - y) > .5f)
				return false;
			if(Math.sqrt((location.x - x) * (location.x - x) + (location. y - y) * ( location.y - y)) < cCheck + r)
				return true;
			return false;
		}
		
		public void add(){
			super.add();
			isActive = false;
		}
	}
	
	class Circle extends Enemy{
		private long timeCheck = 500;
		private short detail = 16;
		private float radius = .2f;
		
		private float dx, dy;
		
		public Circle(float x, float y, float dx, float dy){
			speed = .00125f;
			location = new PointF(x, y);
			
			vertices = new float[detail * 3];
			float theta = (float)((float)2 * 3.1415926) / ((float)detail); 
			float c = (float) Math.cos(theta);
			float s = (float) Math.sin(theta);
			
			float t;

			x = radius;//we start at angle = 0 
			y = 0; 
		    
			for(int i = 0; i < detail * 3; i++) 
			{ 
				vertices[i] = x;
				vertices[i+1] = y;//output vertex 
				vertices[i+2] = 0;
		        
				//apply the rotation matrix
				t = x;
				x = c * x - s * y;
				y = s * t + c * y;
				i+=2;
			} 
		}
		
		public void draw(GL10 gl){
			gl.glColor4f(1, 0, 1, 1);
			
			gl.glPushMatrix();
			
			gl.glTranslatef(location.x, location.y, 0);	
			
			gl.glDisable(GL10.GL_TEXTURE_2D);
			gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
			gl.glFrontFace(GL10.GL_CCW);
			
			gl.glVertexPointer(3, GL10.GL_FLOAT, 0, VertexBuffer.getVertexBuffer(vertices));
			gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, 0, vertices.length / 3);
			
			gl.glColor4f(1, 1, 1, 1);
			gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
			gl.glEnable(GL10.GL_TEXTURE_2D);
			gl.glPopMatrix();
		}
		
		public void update(long time){
			if(isActive){
				if(!inBounds() && GameClock.getInstance().getCurrentTime() - addedTime > timeCheck){
//					Logger.log("Removed Floater");
					isActive = false;
					isDead = true;
				}
				else
					location.set(location.x + dx * time * speed, location.y + dy * time * speed);
			}
			if((location.x < 10 + radius || location.x > 0 - radius) && (location.y < 6 + radius || location.y > 0 - radius))
				isActive = true;
		}
		
		public void setDeltas(float dx, float dy){ this.dx = dx; this.dy = dy; }
		public void add(){ 
			super.add();
			switch((int) (Math.random() * 4)){
			case 0: // Left Wall
				setLoc(0 - radius * .5f, (float) Math.random() * 6);
				setDeltas((float) Math.abs(Math.random()) + 1, (float) (Math.random() < .5 ? -Math.abs(Math.random() + 1) : Math.abs(Math.random() + 1)));
				break;
			case 1:	// Top Wall
				setLoc((float) Math.random() * 10, 6 + radius * .5f);
				setDeltas((float) (Math.random() < .5 ? -Math.abs(Math.random() + 1) : Math.abs(Math.random() + 1)), (float) -Math.abs(Math.random() + 1));
				break;
			case 2: // Right Wall
				setLoc(10 + radius * .5f, (float) Math.random() * 6);
				setDeltas((float) -Math.abs(Math.random() + 1), (float) (Math.random() < .5 ? -Math.abs(Math.random() + 1) : Math.abs(Math.random() + 1)));
				break;
			default: // Bottom
				setLoc((float) Math.random() * 10, 0 - radius * .5f);
				setDeltas((float) (Math.random() < .5 ? -Math.abs(Math.random() + 1) : Math.abs(Math.random() + 1)), (float) 
						Math.abs(Math.random() + 1));
				break;
			}
		}
		
		private boolean inBounds(){
			if(location.x > 10 + radius || location.x < 0 - radius || location.y > 6 + radius || location.x < 0 - radius)
				return false;
			return true;
		}
		
		public boolean collisionCheck(float x, float y, float r){
			if(Math.abs(location.x - x) > .5f || Math.abs(location.y - y) > .5f)
				return false;
			if(Math.sqrt((location.x - x) * (location.x - x) + (location. y - y) * ( location.y - y)) < radius + r)
				return true;
			return false;
		}
	}
	
	class Star extends Enemy{
		private float duration = 750;
		private float[] tradjectory;
		private float[] c;
		private float[] s;
		private float[] archive;
		private float oldX;
		private float spinTime = 1500;
		private float pentagramRadius = .1f;
		private float startRotation;
		private short collisionIterator;
		private boolean shot;
		
		public Star(float x, float y){
			oldX = 0;
			
			rotation = 5000;
			speed = .005f;
			startRotation = (float) (Math.random() * 360);
			tradjectory = new float[5];
			s = new float[5];
			c = new float[5];
			
			location = new PointF(x, y);
			shot = false;
			
			vertices = new float[45];
			for(int z = 2; z < vertices.length; z+=3)
				vertices[z] = 0;

			vertices[0] = vertices[39] = 0;
			vertices[1] = vertices[40] = 1;

			vertices[6] = 1.538f;
			vertices[7] = 2.118f;

			vertices[3] = vertices[9] = 0.951f;
			vertices[4] = vertices[10] = 0.309f;

			vertices[15] = 2.490f;
			vertices[16] = -.809f;

			vertices[12] = vertices[18] = .588f;
			vertices[13] = vertices[19] = -.809f;

			vertices[24] = 0;
			vertices[25] = -2.520f;
			
			vertices[21] = vertices[27] = -.588f;
			vertices[22] = vertices[28] = -.809f;

			vertices[33] = -2.490f;
			vertices[34] = -.809f;

			vertices[30] = vertices[36] = -.951f;
			vertices[31] = vertices[37] = .309f;

			vertices[42] = -1.538f;
			vertices[43] = 2.118f;

			for(int s = 0; s < vertices.length; ++s)
				vertices[s] *= pentagramRadius;
			
			archive = vertices.clone();
			
			for(int r = 0; r < vertices.length; ++r)
				if(r % 3 == 0){ // x
					oldX = vertices[r];
					vertices[r] = (float) (vertices[r] * Math.cos(Math.toRadians(startRotation)) - vertices[r + 1] * Math.sin(Math.toRadians(startRotation)));
				}
				else if(r % 3 == 1) // y
					vertices[r] = (float) (oldX * Math.sin(Math.toRadians(startRotation)) + vertices[r] * Math.cos(Math.toRadians(startRotation)));
			

			for(int t = 0; t < tradjectory.length; ++t){
				tradjectory[t] = 54 - 72 * t + startRotation;
				c[t] = (float) Math.cos(Math.toRadians(tradjectory[t]));
				s[t] = (float) Math.sin(Math.toRadians(tradjectory[t]));
			}
		}
		
		public void draw(GL10 gl){ 
			gl.glColor4f(1, 1, 0, alpha);
			super.draw(gl);
		}
		
		public void update(long time){
			if(shot){ 
				alpha = 1 - ((GameClock.getInstance().getCurrentTime() - (addedTime + (long) spinTime)) / duration);
				for(updateIterator = 0; updateIterator < vertices.length; ++updateIterator){
					if((updateIterator) % 3 == 0){ // x 
						vertices[updateIterator] += c[updateIterator / 9] * time * speed; 
					}
					else if((updateIterator) % 3 == 1){ // y
						vertices[updateIterator] += s[updateIterator / 9] * time * speed;
					}
				}
				if(alpha < .075f){
					isDead = true;
					isActive = false;
//					Logger.log("Removed Star");
				}
			}
			else{
				rotation = 10f / ((GameClock.getInstance().getCurrentTime() - addedTime) / spinTime) * 5 - 50f;
				scale = (5 - (GameClock.getInstance().getCurrentTime() - addedTime) / spinTime * 4);
				alpha = ((GameClock.getInstance().getCurrentTime() - addedTime) / spinTime);
				if(GameClock.getInstance().getCurrentTime() - addedTime > spinTime){
					rotation = 0;
					shot = true;
					alpha = 1;
					scale = 1; 
					shoot();  
				} 
			}
		}
		
		public void shoot(){
			isActive = true;
		}
		
		public boolean collisionCheck(float x, float y, float r){
			if(!isActive)
				return false;
			for(collisionIterator = 0; collisionIterator < vertices.length / 3; ++collisionIterator){
				if(Math.sqrt(((location.x + vertices[collisionIterator * 3]) - (x)) * ((location.x + vertices[collisionIterator * 3]) - (x)) + (location.y + vertices[collisionIterator * 3 + 1] - y) * (location.y + vertices[collisionIterator * 3 + 1] - y)) < r)
					return true;
			}
			return false;
		}
		
		public void add(){
			super.add();
			shot = false;
			startRotation = (float) (Math.random() * 360);
			
			vertices = archive.clone();
			
			for(int r = 0; r < vertices.length; ++r)
				if(r % 3 == 0){ // x
					oldX = vertices[r];
					vertices[r] = (float) (vertices[r] * Math.cos(Math.toRadians(startRotation)) - vertices[r + 1] * Math.sin(Math.toRadians(startRotation)));
				}
				else if(r % 3 == 1) // y
					vertices[r] = (float) (oldX * Math.sin(Math.toRadians(startRotation)) + vertices[r] * Math.cos(Math.toRadians(startRotation)));
			

			for(int t = 0; t < tradjectory.length; ++t){
				tradjectory[t] = 54 - 72 * t + startRotation;
				c[t] = (float) Math.cos(Math.toRadians(tradjectory[t]));
				s[t] = (float) Math.sin(Math.toRadians(tradjectory[t]));
			}
		}
	}
	
	class GrowingSquare extends Enemy{		
		private long growthDuration = 500;
		private long growthStart;
		private float fadeIn = 1000;
		private long activeTime;
		private long delay = 1000;

		private float halfWidth = .25f;
		
		public GrowingSquare(float x, float y, float rotation){
			location = new PointF(x, y);
			
			vertices = new float[72];
			for(int z = 2; z < vertices.length; z+=3)
				vertices[z] = 0;
			
			vertices[0] = -.25f;
			vertices[1] = .25f;
			vertices[3] = vertices[9] = -.25f;
			vertices[4] = vertices[10] = .2f;
			vertices[6] = vertices[15] = .2f;
			vertices[7] = vertices[16] = .25f;
			
			vertices[12] = .2f;
			vertices[13] = .2f;
			
			vertices[18] = .2f;
			vertices[19] = .25f;
			vertices[21] = vertices[27] = .2f;
			vertices[22] = vertices[28] = -.2f;
			vertices[24] = vertices[33] = .25f;
			vertices[25] = vertices[34] = .25f;
			
			vertices[30] = .25f;
			vertices[31] = -.2f;
			
			vertices[36] = -.2f;
			vertices[37] = -.2f;
			vertices[39] = vertices[45] = -.2f;
			vertices[40] = vertices[46] = -.25f;
			vertices[42] = vertices[51] = .25f;
			vertices[43] = vertices[52] = -.2f;
			
			vertices[48] = .25f;
			vertices[49] = -.25f;
			
			vertices[54] = -.25f;
			vertices[55] = -.25f;
			vertices[57] = vertices[63] = -.2f;
			vertices[58] = vertices[64] = -.25f;
			vertices[60] = vertices[69] = -.25f;
			vertices[61] = vertices[70] = .2f;
			
			vertices[66] = -.2f;
			vertices[67] = .2f;
		}
		public void draw(GL10 gl){
			gl.glColor4f(0, 1, 0, alpha);
			super.draw(gl);
		}
		public void update(long time){
			if(isActive){
				if(GameClock.getInstance().getCurrentTime() - activeTime > delay){
					scale = 1 + (GameClock.getInstance().getCurrentTime() - growthStart) / 125f;
					alpha = 1 - (GameClock.getInstance().getCurrentTime() - growthStart) / (float) growthDuration;
				}
				if(alpha < .075f){
					isDead = true;
					isActive = false;
				}
			}
			else{ 
				scale = 1f / ((GameClock.getInstance().getCurrentTime() - addedTime) / fadeIn) * 1 - 0f;
				alpha = ((GameClock.getInstance().getCurrentTime() - addedTime) / fadeIn);
				if(GameClock.getInstance().getCurrentTime() - addedTime > fadeIn){
					rotation = 0;
					alpha = 1;
					scale = 1; 
					isActive = true; 
					activeTime = GameClock.getInstance().getCurrentTime();
					growthStart = activeTime + delay;
				} 
			}
		}
		
		public boolean collisionCheck(float x, float y, float r){
			if(!isActive)
				return false;
			if(location.x - halfWidth * scale < x + r && location.x + halfWidth* scale > x - r)
				if(location.y - halfWidth * scale < y + r && location.y + halfWidth * scale > y - r)
					return true; 
			return false;
		}
	}
	
	class Enemy{
		protected PointF location;
		protected float rotation;
		protected float speed; 
		protected float scale = 1;
		float[] vertices;
		float alpha = 1;
		protected boolean isActive;
		protected boolean isDead = false;
		protected short updateIterator;
		
		protected long addedTime;
		
		public Enemy(){
			
		}
		
		public void draw(GL10 gl){
			gl.glPushMatrix();
			
			gl.glTranslatef(location.x, location.y, 0);	
			gl.glRotatef(rotation, 0, 0, 1);
			gl.glScalef(scale, scale, 1);
			
			gl.glDisable(GL10.GL_TEXTURE_2D);
			gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
			gl.glFrontFace(GL10.GL_CCW);
			
			gl.glVertexPointer(3, GL10.GL_FLOAT, 0, VertexBuffer.getVertexBuffer(vertices));
			gl.glDrawArrays(GL10.GL_TRIANGLES, 0, vertices.length / 3);
			
			gl.glColor4f(1, 1, 1, 1);
			gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
			gl.glEnable(GL10.GL_TEXTURE_2D);
			gl.glPopMatrix();
		}

		public void setLoc(float x, float y){ location.set(x, y); }
		
		public void add(){ isDead = false; addedTime = GameClock.getInstance().getCurrentTime(); }
		public boolean isDead(){ return isDead; }
		public void reset(){ isActive = false; isDead = true; }
	}
	
	class EnemyManager{
		private Arrow[] arrows;
//		private Cluster[] clusters;
		private ArcSquare[] arcSquares;
		private Star[] stars;
		private Circle[] floaters;
		private GrowingSquare[] growers;

		// [Floaters, Growers, Star, Arrows, Arc]
		private long[] minWaitTimes = {500, 500, 250, 250, 500};
		// [Floaters, Growers, Star, Arrows, Arc]
		private long[] maxWaitTimes = {4000, 10000, 7500, 1000, 2500};
		// [Floaters, Growers, Star, Arrows, Arc]
		private long[] nextSpawns = {0, 0, 0, 0, 0};
		
		private short addIterator;
		private short drawIterator, updateIterator, collisionIterator, stuffIterator;
		private short arraySizes = 50;

		private byte randomByte;

		private boolean[] dirtyArrows;
//		private boolean[] dirtyClusters;
		private boolean[] dirtySquares;
		private boolean[] dirtyStars;
		private boolean[] dirtyFloaters;
		private boolean[] dirtyGrowers;
		
		public EnemyManager(){
			arrows = new Arrow[arraySizes];
			dirtyArrows = new boolean[arraySizes];
			growers = new GrowingSquare[arraySizes];
			dirtyGrowers = new boolean[arraySizes];
			arcSquares = new ArcSquare[arraySizes];
			dirtySquares = new boolean[arraySizes];
			stars = new Star[arraySizes];
			dirtyStars = new boolean[arraySizes];
			floaters = new Circle[arraySizes];
			dirtyFloaters = new boolean[arraySizes];
			
			for(int i = 0; i < arraySizes; ++i){
				arrows[i] = new Arrow(0, 0, 0);
				dirtyArrows[i] = false;
				arcSquares[i] = new ArcSquare(0, 0, 0, false);
				dirtySquares[i] = false;
				stars[i] = new Star(0, 0);
				dirtyStars[i] = false;
				floaters[i] = new Circle(0, 0, 0, 0);
				dirtyFloaters[i] = false;
				growers[i] = new GrowingSquare(0, 0, 0);
				dirtyGrowers[i] = false;
			}
//			clusters = new Cluster[arraySizes];
//			dirtyClusters = new boolean[arraySizes];
//			for(int i = 0; i < arraySizes; ++i){
//				clusters[i] = new Cluster(0, 0, 0, 0, 0);
//				dirtyClusters[i] = false;
//			}
		}
		
		public void draw(GL10 gl){
			for(drawIterator = 0; drawIterator < arraySizes; ++drawIterator){
				if(dirtyArrows[drawIterator])
					arrows[drawIterator].draw(gl);
//				if(dirtyClusters[drawIterator])
//					clusters[drawIterator].draw(gl);
				if(dirtySquares[drawIterator])
					arcSquares[drawIterator].draw(gl);
				if(dirtyStars[drawIterator])
					stars[drawIterator].draw(gl);
				if(dirtyGrowers[drawIterator])
					growers[drawIterator].draw(gl);
				if(dirtyFloaters[drawIterator])
					floaters[drawIterator].draw(gl);
			}
		}
		
		public void update(long time){
			for(updateIterator = 0; updateIterator < arraySizes; ++updateIterator){
				if(dirtyArrows[updateIterator]){
					if(arrows[updateIterator].isDead()){
						dirtyArrows[updateIterator] = false;
//						Logger.log("Cleaned Arrow Bit");
					}
					else arrows[updateIterator].update(time);
				}
//				if(dirtyClusters[updateIterator])
//					clusters[updateIterator].update(time); 
				if(dirtySquares[updateIterator]){
					if(arcSquares[updateIterator].isDead()){
						dirtySquares[updateIterator] = false;
//						Logger.log("Cleaned Arc Bit");
					}
					else arcSquares[updateIterator].update(time);
				}
				if(dirtyStars[updateIterator]){
					if(stars[updateIterator].isDead()){
						dirtyStars[updateIterator] = false;
//						Logger.log("Cleaned Star Bit");
					}
					else stars[updateIterator].update(time);
				}
				if(dirtyGrowers[updateIterator]){
					if(growers[updateIterator].isDead()){
						dirtyGrowers[updateIterator] = false;
//						Logger.log("Cleaned Grower Bit");
					}
					else growers[updateIterator].update(time);
				}
				if(dirtyFloaters[updateIterator]){
					if(floaters[updateIterator].isDead()){
						dirtyFloaters[updateIterator] = false; 
//						Logger.log("Cleaned Floater Bit");
					}
					else floaters[updateIterator].update(time);
				}
			}
		}
		
		public void addStuff(long timer){
			if(timer - nextSpawns[0] > 0 ){
				for(stuffIterator = 0; stuffIterator <= Math.random() * timer / 5000; ++stuffIterator){
					nextSpawns[0] = (long) (timer + Math.random() * (maxWaitTimes[0] - minWaitTimes[0]) + minWaitTimes[0]);
					addFloater();
				}
			}
			if(timer - nextSpawns[1] > 0 ){
				for(stuffIterator = 0; stuffIterator <= Math.random() * timer / 3500; ++stuffIterator){
					nextSpawns[1] = (long) (timer + Math.sqrt(Math.random()) * (maxWaitTimes[1] - minWaitTimes[1]) + minWaitTimes[1]);
					addGrower((float) (.5f + Math.random() * 9), (float) (.5f + Math.random() * 5));
				}
			}
			if(timer - nextSpawns[2] > 0 ){
				for(stuffIterator = 0; stuffIterator <= Math.random() * timer / 4000; ++stuffIterator){
					nextSpawns[2] = (long) (timer + Math.sqrt(Math.random()) * (maxWaitTimes[2] - minWaitTimes[2]) + minWaitTimes[2]);
					addStar((float) (.5f + Math.random() * 9), (float) (.5f + Math.random() * 5));
				}
			} 
			if(timer - nextSpawns[3] > 0 ){
				for(stuffIterator = 0; stuffIterator <= Math.random() * timer / 5000; ++stuffIterator){
					nextSpawns[3] = (long) (timer + Math.sqrt(Math.random()) * (maxWaitTimes[3] - minWaitTimes[3]) + minWaitTimes[3]);
					randomByte = (byte) (Math.random() * 4);
					addArrow((float) (Math.random() * ((randomByte % 2) == 0 ? 6 : 10)), randomByte);
				}
			} 
			if(timer - nextSpawns[4] > 0 ){
				for(stuffIterator = 0; stuffIterator <= Math.random() * timer / 6000; ++stuffIterator){
					nextSpawns[4] = (long) (timer + Math.sqrt(Math.random()) * (maxWaitTimes[4] - minWaitTimes[4]) + minWaitTimes[4]);
					randomByte = (byte)(Math.random() * 4);
					addArc((float) (((randomByte % 2 == 0) ? 6 : 10) * Math.random()), Math.random() < .5 ? true : false, randomByte);
				}
			} 
		}
		
		public void addArrow(float d, byte start){
			for(addIterator = 0; addIterator < arraySizes; ++addIterator){
				if(dirtyArrows[addIterator] == false){
					dirtyArrows[addIterator] = true;
					arrows[addIterator].add();
					switch(start){
					case 0: // Left
						arrows[addIterator].setLoc(0, d);
						arrows[addIterator].setRotation(-90);
						break;
					case 1: // Top
						arrows[addIterator].setLoc(d, 6);
						arrows[addIterator].setRotation(180);
						break;
					case 2: // Right
						arrows[addIterator].setLoc(10, d);
						arrows[addIterator].setRotation(90);
						break;
					default: // Bottom
						arrows[addIterator].setLoc(d, 0);
						arrows[addIterator].setRotation(0);
						break;
					}
					break;
				}
			}
		}
		
		public void addStar(float x, float y){
			for(addIterator = 0; addIterator < arraySizes; ++addIterator){
				if(dirtyStars[addIterator] == false){
					dirtyStars[addIterator] = true;
					stars[addIterator].setLoc(x, y);
					stars[addIterator].add();
					break;
				}
			}
		}
		
//		public void addCluster(float x, float y, float fuse){
//			for(addIterator = 0; addIterator < arraySizes; ++addIterator){
//				if(dirtyClusters[addIterator] == false){
//					dirtyClusters[addIterator] = true;
//					clusters[addIterator].setLoc(x, y);
//					clusters[addIterator].setFuse((float) (Math.random() * 3000 + 3000));
//					clusters[addIterator].add();
//					break;
//				}
//			}
//		}
		
		public void addFloater(){
			for(addIterator = 0; addIterator < arraySizes; ++addIterator){
				if(dirtyFloaters[addIterator] == false){
					dirtyFloaters[addIterator] = true;
					floaters[addIterator].add();
					break;
				}
			}
		}
		
		public void addGrower(float x, float y){
			for(addIterator = 0; addIterator < arraySizes; ++addIterator){
				if(dirtyGrowers[addIterator] == false){
					dirtyGrowers[addIterator] = true;
					growers[addIterator].setLoc(x, y);
					growers[addIterator].add();
					break;
				}
			}
		}
		
		public void addArc(float r, boolean cw, byte start){
			for(addIterator = 0; addIterator < arraySizes; ++addIterator){
				if(dirtySquares[addIterator] == false){
					dirtySquares[addIterator] = true;
					switch(start){
					case 0: // Left
						arcSquares[addIterator].setLoc(0, cw ? r : 6 - r );
						break;
					case 1: // Top
						arcSquares[addIterator].setLoc(cw ? r : 10 - r , 6);
						break;
					case 2: // Right
						arcSquares[addIterator].setLoc(10, cw ? 6 - r : r );
						break;
					default: // Bottom
						arcSquares[addIterator].setLoc(cw ? 10 - r : r , 0);
						break;
					}
					arcSquares[addIterator].setRadius(r, cw, (byte) start);
					arcSquares[addIterator].add();
					break;
				}
			}
		}
		
		public boolean collisionCheck(Player p){
			for(collisionIterator = 0; collisionIterator < arraySizes; ++collisionIterator){
				if(dirtySquares[collisionIterator]){
					if(arcSquares[collisionIterator].collisionCheck(p.getLoc().x, p.getLoc().y, p.getRadius()))
						return true;
				}
				if(dirtyFloaters[collisionIterator]){
					if(floaters[collisionIterator].collisionCheck(p.getLoc().x, p.getLoc().y, p.getRadius()))
						return true;
				}
				if(dirtyGrowers[collisionIterator]){ 
					if(growers[collisionIterator].collisionCheck(p.getLoc().x, p.getLoc().y, p.getRadius()))
						return true;
				}
				if(dirtyStars[collisionIterator]){ 
					if(stars[collisionIterator].collisionCheck(p.getLoc().x, p.getLoc().y, p.getRadius()))
						return true;
				}
				if(dirtyArrows[collisionIterator]){ 
					if(arrows[collisionIterator].collisionCheck(p.getLoc().x, p.getLoc().y, p.getRadius()))
						return true;
				}
			}
			return false;
		}
		
		public void reset(){
			for(int i = 0; i < arraySizes; ++i){
				dirtyArrows[i] = false;
				arrows[i].reset();
				dirtySquares[i] = false;
				arcSquares[i].reset();
				dirtyStars[i] = false;
				stars[i].reset();
				dirtyFloaters[i] = false;
				floaters[i].reset();
				dirtyGrowers[i] = false;
				growers[i].reset();
			}
			for(int i = 0; i < nextSpawns.length; ++i)
				nextSpawns[i] = 0;
		}
	}
}